iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Software Development

渲染與GPU編程系列 第 16

Day 15|深入 Descriptor Set 與 Pipeline Layout (延伸)

  • 分享至 

  • xImage
  •  

上一篇已經做完了基礎Vulkan教學,這篇是補充資訊。

這篇要把兩個最常卡人的名詞講到清楚:Descriptor SetPipeline Layout
先用生活比喻建立概念,再一步步做一個「一個 UBO + 一張貼圖」的最小可跑範例,最後補上設計心法(全域/材質/物件分層、動態 UBO、Push Constant)與除錯清單。
我們沿用你前面章節的渲染骨架(傳統 Render Pass + Framebuffer,不用 dynamic rendering)。


1)先用比喻:插座、延長線、家電

  • Descriptor(描述符):就像插頭標籤,說明「這條線是誰(Buffer / Image / Sampler)、可以在哪些階段用(VS/FS…)」
  • Descriptor Set:一包插頭的集合,像一條延長線
  • Descriptor Set Layout:這條延長線上插孔的規格(第 0 孔放 UBO、第 1 孔放貼圖…)。
  • Pipeline Layout:把多條延長線的規格(多個 Set Layout)和 Push Constant 範圍打包;它是建立 Graphics Pipeline 時必填的「接線板規格」。
  • 繪製時你會把某一條真正的「延長線」(Descriptor Set 實例)接到「接線板」(Pipeline),GPU 才知道著色器要用到哪個 Buffer/Texture。

2)名詞對上:set / binding 是什麼?

  • 在 GLSL 你會看到 layout(set = X, binding = Y)

    • set:第幾條延長線(0, 1, 2…)。
    • binding:那條線上的第幾個插孔(0, 1, 2…)。
  • 你在 C++ 端建立 DescriptorSetLayout 時,會宣告每個 binding 的型別/數量/可見階段
    建好 Pipeline Layout 後,只能綁定符合該規格的 Descriptor Set


3)範例

  • Set 0 / binding 0UBO(攝影機/光)→ VS 與 FS 都要看
  • Set 1 / binding 0combined image sampler(一張貼圖 + 一個取樣器)→ 只在 FS 用

這樣的分層有個好處:

  • Set 0(每幀都換)稱為全域資料
  • Set 1(材質)比較少換,可以重複使用在多個模型上

4)設計心法:怎麼切 Set 才好用?

常見 3 層分法:

  1. Set 0(Per-Frame / Global):相機矩陣、環境燈、時間…→ 每幀更新一次。
  2. Set 1(Per-Material):貼圖、材質參數(albedo/roughness/metallic…)→ 同材質可共用。
  3. Set 2(Per-Object):每個物件的 model/normalMat,或索引到大型結構(例如 SSBO)→ 可能每 draw 都改。

這樣做的好處:綁定頻率清楚(綁得越少的放前面),CPU 指令較少;
此外,Pipeline Layout 不用常換,切換材質或物件時只換對應的 Set。


5)Push Constant vs UBO(什麼時候用哪個?)

  • Push Constant極少量(通常 ≤128Byte)每 draw 快速改的數據。像「這次 draw 的顏色/索引」。
  • UBO:可以放比較大的矩陣與常數;更新頻率較低或使用環形記憶體更新。
  • 新手路線:先用 UBO 全包,之後感到瓶頸再把最常改的小量數據搬到 Push Constant

6)Dynamic UBO(進階一點點)

當你有很多物件,又不想為每個物件分配一個 Descriptor Set,可以用
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC

  • 一個大 UBO 裡裝很多筆資料;
  • 綁定同一個 Descriptor Set;
  • 每次 vkCmdBindDescriptorSets 時用 dynamic offset 指到本次要用的那段。

這能大幅減少「換 Set」的次數,但需要管理好對齊與 offset。


7)常見錯誤與快速排除

  1. 畫面不變/資料沒進 Shader

    • set/binding 號碼對不上:GLSL 的 layout(set=?, binding=?) 要跟 DescriptorSetLayoutBinding 完全一致。
    • 綁錯 firstSet
  2. Validation 說 DescriptorType 不相容

    • 例如你在 GLSL 用 sampler2D,C++ 卻用 SAMPLED_IMAGESAMPLER,正確是 COMBINED_IMAGE_SAMPLER
  3. 貼圖全黑

    • 沒把影像 layout 轉成 SHADER_READ_ONLY_OPTIMAL(這個 Render Pass 不會幫你做)。
    • VkDescriptorImageInfo.imageLayout 寫錯。
  4. 改了 Layout 但 Pipeline 不重建

    • Pipeline Layout 變更 → 該 Graphics Pipeline 也要重建
  5. std140 對齊錯

    • UBO 用 mat4/vec4 保險;vec3 會補一格(等於 vec4 的大小)。
  6. Descriptor Pool 不夠

    • maxSetspoolSizes 太小;請預估幀數/材質數,或多開幾個 Pool。
  7. 重建 swapchain 後貼圖失效

    • 若材質或取樣器沒動,通常不用重建;但如果你重建過 image view 或改了格式,一定要重新更新 Descriptor

8)一句話總結

Descriptor Set 是「把 Buffer/Texture 接到 Shader 的延長線」;
Pipeline Layout 是「告訴 Pipeline 這條延長線的規格」。
先把 全域(Set0)材質(Set1)物件(Set2) 分清楚,搭好 Layout → Pool → Set → Update → Bind 這條流水線,你的 Vulkan 程式就能穩定、有條理地把資料送進 GPU,後面加 PBR、骨骼動畫、陰影貼圖就不會亂。

至此,Vulkan的教學結束。


上一篇
Day 14|Vulkan 載入模型並加入光影
下一篇
Day 16|WebGPU 是什麼?與 WebGL 的差異與優勢
系列文
渲染與GPU編程22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言